/*
 * jxSwing Project (2014)
 * 
 * by Denilson Edinaldo Pinto
 * 
 * LGPL
 * Copyright (C) 2014
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.jxSwing.component.panel;

import java.io.File;
import java.io.IOException;
import java.util.List;

import javax.swing.JScrollPane;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeSelectionModel;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.jxSwing.component.tree.FileTree;
import org.jxSwing.component.tree.FileTreeModel;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * @author Denilson Edinaldo Pinto
 * @porject jxSwing
 * @package org.jxSwing.component.panel
 * @date 15/06/2014 - 10:09:20
 */
public class FileTreePanel extends JScrollPane {
	
	private static final long serialVersionUID = -1785895724812972382L;
	
	private final File root;
	
	private final boolean showHideFiles;
	
	private final boolean showOnlyDirectories;
	
	private FileTree fileTree;
	
	/**
	 * 
	 */
	public FileTreePanel() {
	
		this(false);
	}
	
	/**
	 * @param showHideFiles
	 */
	public FileTreePanel(final boolean showHideFiles) {
	
		this(null, showHideFiles, false);
	}
	
	/**
	 * @param showHideFiles
	 * @param showOnlyDirectories
	 */
	public FileTreePanel(final boolean showHideFiles, final boolean showOnlyDirectories) {
	
		this(null, showHideFiles, showOnlyDirectories);
	}
	
	/**
	 * @param root
	 * @param showHideFiles
	 * @param showOnlyDirectories
	 */
	public FileTreePanel(final File root, final boolean showHideFiles, final boolean showOnlyDirectories) {
	
		this.root = root;
		this.showHideFiles = showHideFiles;
		this.showOnlyDirectories = showOnlyDirectories;
		
		setViewportView(getFileTree());
	}
	
	/**
	 * <b>Reference:</b> http://cse.unl.edu/~reich/XML/DOM/XML2JTree.java <br>
	 * <br>
	 * This takes a DOM Node and recurses through the children until each one is
	 * added to a DefaultMutableTreeNode. This can then be used by the JTree as a
	 * tree model. The second parameter can be used to provide more visual detail
	 * for debugging.
	 */
	protected final DefaultMutableTreeNode createTreeNode(final Node root, final boolean showDetails) {
	
		DefaultMutableTreeNode dmtNode = null;
		
		final String type = getNodeType(root);
		final String name = root.getNodeName();
		final String value = root.getNodeValue();
		
		if(showDetails) {
			
			dmtNode = new DefaultMutableTreeNode("[" + type + "] --> " + name + " = " + value);
			
		}else {
			
			// Special case for TEXT_NODE, others are similar but not catered for here.
			dmtNode = new DefaultMutableTreeNode(root.getNodeType() == Node.TEXT_NODE ? value : name);
		}
		
		// Display the attributes if there are any
		final NamedNodeMap attribs = root.getAttributes();
		
		//
		if(attribs != null && showDetails) {
			
			//
			for(int i = 0; i < attribs.getLength(); i++) {
				
				final Node attNode = attribs.item(i);
				final String attName = attNode.getNodeName().trim();
				final String attValue = attNode.getNodeValue().trim();
				
				//
				if(attValue != null) {
					
					//
					if(attValue.length() > 0) {
						
						dmtNode.add(new DefaultMutableTreeNode("[Attribute] --> " + attName + "=\"" + attValue + "\""));
					}
				}
			}
		}
		
		// If there are any children and they are non-null then recurse...
		if(root.hasChildNodes()) {
			
			final NodeList childNodes = root.getChildNodes();
			
			//
			if(childNodes != null) {
				
				//
				for(int k = 0; k < childNodes.getLength(); k++) {
					
					final Node nd = childNodes.item(k);
					
					//
					if(nd != null) {
						
						// A special case could be made for each Node type.
						if(nd.getNodeType() == Node.ELEMENT_NODE) {
							
							dmtNode.add(createTreeNode(nd, showDetails));
						}
						
						// This is the default
						String data = nd.getNodeValue();
						
						//
						if(data != null) {
							
							data = data.trim();
							
							//
							if( !data.equals("\n") && !data.equals("\r\n") && data.length() > 0) {
								
								dmtNode.add(createTreeNode(nd, showDetails));
							}
						}
					}
				}
			}
		}
		
		return dmtNode;
	}
	
	/**
	 * @param file
	 * @throws IOException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 */
	public final void fromXML(final File file) throws IOException, ParserConfigurationException, SAXException {
	
		getFileTree().fromXML(file);
	}
	
	/**
	 * @return the fileTree
	 */
	public FileTree getFileTree() {
	
		if(fileTree == null) {
			fileTree = new FileTree(root, showHideFiles, showOnlyDirectories);
		}
		return fileTree;
	}
	
	/**
	 * @return
	 */
	public int getIndex() {
	
		return getFileTree().getIndex();
	}
	
	/**
	 * Returns the TreeModel that is providing the data.
	 * 
	 * @return the TreeModel that is providing the data
	 */
	public FileTreeModel getModel() {
	
		return getFileTree().getModel();
	}
	
	/**
	 * @param node
	 * @return type
	 */
	public final String getNodeType(final Node node) {
	
		return getFileTree().getNodeType(node);
	}
	
	/**
	 * @return the root
	 */
	public File getRoot() {
	
		return getFileTree().getRoot();
	}
	
	/**
	 * @return the selectedFile or null
	 */
	public File getSelectedFile() {
	
		return getFileTree().getSelectedFile();
	}
	
	/**
	 * @return the selectedFiles
	 */
	public List<File> getSelectedFiles() {
	
		return getFileTree().getSelectedFiles();
	}
	
	/**
	 * Returns the model for selections. This should always return a
	 * non-<code>null</code> value. If you don't want to allow anything
	 * to be selected
	 * set the selection model to <code>null</code>, which forces an empty
	 * selection model to be used.
	 * 
	 * @see #setSelectionModel
	 */
	public TreeSelectionModel getSelectionMode() {
	
		return getFileTree().getSelectionModel();
	}
	
	/**
	 * 
	 */
	public final void refresh() {
	
		getFileTree().refresh();
	}
	
	/**
	 * @param file
	 */
	public void setSelectedFile(final File file) {
	
		getFileTree().setSelectionPath(getFileTree().fileToTreePath(file));
	}
	
	/**
	 * @param files
	 */
	public void setSelectedFiles(final List<File> files) {
	
		getFileTree().setSelectionPaths(getFileTree().filesToTreePaths(files));
	}
	
	/**
	 * Sets the selection model, which must be one of SINGLE_TREE_SELECTION,
	 * CONTIGUOUS_TREE_SELECTION or DISCONTIGUOUS_TREE_SELECTION.
	 * <p>
	 * This may change the selection if the current selection is not valid for the new mode. For example, if three TreePaths are selected when the mode is changed to <code>SINGLE_TREE_SELECTION</code> , only one TreePath will remain selected. It is up to the particular implementation to decide what TreePath remains selected.
	 */
	public void setSelectionMode(final int mode) {
	
		getFileTree().setSelectionMode(mode);
	}
	
	/**
	 * @return the showHideFiles
	 */
	public final boolean showHideFiles() {
	
		return getFileTree().showHideFiles();
	}
	
	/**
	 * @return the showOnlyDirectories
	 */
	public final boolean showOnlyDirectories() {
	
		return getFileTree().showOnlyDirectories();
	}
	
	/**
	 * @return xml String
	 * @throws ParserConfigurationException
	 * @throws TransformerException
	 */
	public final String toXML() throws ParserConfigurationException, TransformerException {
	
		return getFileTree().toXML();
	}
	
	/**
	 * @param file
	 * @throws TransformerException
	 * @throws ParserConfigurationException
	 * @throws IOException
	 */
	public final void toXML(final File file) throws IOException, ParserConfigurationException, TransformerException {
	
		getFileTree().toXML(file);
	}
}
